#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <fcntl.h>


#define SIZE 1024     //字符串命令行大小
#define MAX_ARGV 64   //分割大小
#define SEP " "       //分割符号
#define STREND '\0'   //字符串结束标志符

//重定向有关的
#define NoneRedir  -1
#define StdinRedir  0
#define StdoutRedir 1
#define AppendRedir 2
#define IgnSpace(buf,pos) do{while(isspace(buf[pos])) pos++;}while(0)    //用do——while是为了可以后面加 ;

int redir_type = NoneRedir;  //重定向标志
char* filename = NULL;        //文件名

char *argv[MAX_ARGV]; 
char pwd[SIZE];
char env[SIZE];
int lastcode = 0;     //退出码

//获取服务器信息
const char* HostName()
{
  char* hostname = getenv("HOSTNAME");
  if(hostname) return hostname;
  else return "None";
}

//获取用户名
const char* UserName()
{
  char* username = getenv("USER");
  if(username) return username;
  else return "None";
}

//获取当前目录
const char* CurrentWorkDir()
{
  char* pwd = getenv("PWD");
  if(pwd) return pwd;
  else return "None";
}

char* Home()
{
  return getenv("HOME");
}

//输出提示符和获取输入命令
int Interactive(char out[],int size)
{
    
     printf("[%s@%s %s] $",UserName(),HostName(),CurrentWorkDir());
     fgets(out,size,stdin);     //获取用户输入的命令
     out[strlen(out)-1] = 0;
     return strlen(out);
}

// ls -l -a 
// ls -l -a > log.txt
// ls -l -a >> log.txt
// cat < log.txt
//把字符串分割成两部分，一部分是指令，一部分文件名，还有判断是什么重定向
void CheckRedir(char in[])
{
    redir_type = NoneRedir;
    filename = NULL;
    int pos = strlen(in) - 1;
    while(pos > 0)
    {
        if(in[pos] == '>')
        {
            if(in[pos - 1] == '>')
            {
                redir_type = AppendRedir;
                in[pos++] = STREND;
                pos++;
                IgnSpace(in,pos);
                filename = in+pos;
                break;
            }
            else
            {
                redir_type = StdoutRedir;
                in[pos++] = STREND;
                IgnSpace(in,pos);
                filename = in+pos;
                break;
            }
        }
        else if(in[pos] == '<')
        {
            redir_type = StdinRedir;
            in[pos++] = STREND;
            IgnSpace(in,pos);
            filename = in+pos;
            break;
        }
        else
        {
            pos--;
        }
    }
}

//分割字符串命令
void Split(char in[])
{
  CheckRedir(in);     //检测重定向

  int i = 0;
  argv[i++] = strtok(in,SEP);
  while(argv[i++] = strtok(NULL,SEP));
  argv[i] = NULL;
  if(strcmp(argv[0],"ls") == 0)
  {
      argv[i-1] = (char*)"--color";
      argv[i] = NULL;
  }
}

int Buildincmd()
{
  int ret = 0;
  //检测是否为内建命令,是返回1，不是放回0
  if(strcmp("cd",argv[0]) == 0)
  {
    //执行
    ret = 1;
    char* target = argv[1];
    if(target == NULL)
    {
      target = Home();
    }
    chdir(target);
    char tmp[1024];
    getcwd(tmp,1024);
    snprintf(pwd,SIZE,"PWD=%s",tmp);
    putenv(pwd);
  }
  else if(strcmp(argv[0],"export") == 0)
  {
    ret = 1;
    if(argv[1])
    {
      strcpy(env,argv[1]);  //这里只是简单用添加一个来实现，如果要添加多的环境变量，应该要开辟空间，因为环境变量就是系统给开辟一块空间单独存放的。
      putenv(env);
    }
  }
  else if(strcmp(argv[0],"echo") == 0)
  {
    ret = 1;
    if(argv[1] == NULL)
    {
      printf("\n");
    }
    else
    {
      if(argv[1][0] == '$')
      {
        if(argv[1][1] == '?')
        {
          printf("%d\n",lastcode);
          lastcode = 0;
        }
        else
        {
          char* a = getenv(argv[1]+1);
          if(a) printf("%s\n",a);

        }
      }
      else{
        printf("%s",argv[1]);
      }
    }
  }
  return ret;
}


//执行程序
void Execute()
{
  pid_t id = fork();
  if(id == 0)
  {
    int fd = -1;
    if(redir_type == StdinRedir)
    {
        fd = open(filename,O_RDONLY);
        dup2(fd,0);
    }
    else if(redir_type == StdoutRedir)
    {
        umask(0);
        fd = open(filename,O_CREAT|O_WRONLY|O_TRUNC,0666);
        dup2(fd,1);
    }
    else if(redir_type == AppendRedir)
    {
        umask(0);
        fd = open(filename,O_CREAT|O_APPEND|O_WRONLY,0666);
        dup2(fd,1);
    }

    execvp(argv[0],argv);   //进程替换
    exit(1);
  }
  int status = 0;
  pid_t rid = waitpid(id,&status,0);
  if(rid == id) lastcode = WEXITSTATUS(status);
}

int main()
{
  //主循环，让程序可以一直运行
  while(1)
    {
     char commandline[SIZE];
     //1、打印命令行提示符,获取用户输入的字符串命令
     int n = Interactive(commandline,SIZE);
     if(n == 0) continue;   //处理空命令问题
     //2、对字符串命令行进行分割
     Split(commandline);
     //3、执行内建命令
     n = Buildincmd();
     if(n == 1) continue;
     //4、执行程序
     Execute();
    }
  return 0;
}
